#include "includes.h"

const wchar_t* Path = L".\\dll";			//L denotes wide string. Defines where to find the DLLs

int main()
{	
	
	std::string FileName = "SimplePluginLib.dll";
	std::string FileName2 = "SimpleObjLib.dll";
	HINSTANCE hInstance = NULL;
	if ((hInstance = LoadDLL(FileName)) != NULL)
	{
		CallSimpleFn(hInstance);
		::FreeLibrary(hInstance);
	}
	
	if ((hInstance = NULL) == NULL)
		std:: cout << "> hInstance freed" << std::endl;
	if ((hInstance = LoadDLL(FileName2)) != NULL)
	{
		CallSimpleObj(hInstance);
		::FreeLibrary(hInstance);
	}

	getchar();

	return 0;
}


HINSTANCE LoadDLL(std::string FileName)
{	
	using namespace std;
	HINSTANCE rc = NULL;
	try
	{	
		LPCWSTR Path_w= Path;
		SetDllDirectory(Path_w);
		if(FileName.c_str() == '\0')
			cout << "Error: Invalid Filename" << endl;
		else
		{
			wchar_t	wcTempBuffer[500+1];
			const wchar_t *pwStrPathFileName = '\0';

			if ((pwStrPathFileName = ConvertStrTowStr(wcTempBuffer, 500, FileName.c_str(), (size_t)500/sizeof(wchar_t))) == '\0')
				cout << "Error: unable to convert pathfilenameDll" << std::endl;
			else
			{
				if((rc = ::LoadLibrary(wcTempBuffer)) == NULL)
					std::cout << "Error: unable to load lib" << std::endl;
				else
					cout << "> DLL Loaded!" << endl;
			}
		}
	}
	catch(...)
	{
		cout << "Error: unexpected exception" << endl;
	}
	
	return rc;
}

void CallSimpleObj(HINSTANCE hInstance)
{	
	using namespace std;
	ISimpleObj *Obj = '\0';
	ISimpleObj * (*pfn)(void) = 0;			//type of this pointer has to be the same as "SimpleFunction"
	try 
	{
		if (pfn == '\0')
		{
			cout << " >> Initialising pointer and calling function..." << endl;
		}

		*(FARPROC *)&pfn = ::GetProcAddress(hInstance, "Create");
	
		if (pfn == '\0')
		{
			cout << "pfn not initialised" << endl;
			cout << "Error: " << ::GetLastError() << endl;
		}
		else
		{	
			Obj = pfn();
			cout << " >> pfn initialised successfully!" << endl;
			cout << Obj->ReturnSimpleObj() << endl;					//(*Obj).ReturnSimpleObj()
			Obj->Delete();
		}
	}
	catch(...)
	{
		cout << "Error: unexpected exception" << endl;
	}
	//Can return Obj. 
}

void CallSimpleFn(HINSTANCE hInstance)
{
	using namespace std;
	const char* (*pfn)(void) = 0;			//type of this pointer has to be the same as "SimpleFunction"
	try{
		if (pfn == '\0')
		{
			cout << " >> Initialising pointer and calling function..." << endl;
		}

		*(FARPROC *)&pfn = ::GetProcAddress(hInstance, "SimpleFunction");

		if (pfn == '\0')
		{
			cout << "pfn not initialised" << endl;
			cout << "Error: " << ::GetLastError() << endl;
		}
		else
		{
			cout << " >> pfn initialised successfully!" << endl;
			cout << pfn() << endl;
		}
	}
	catch (...)
	{
		cout << "Error: unexpected exception" << endl;
	}
}

const wchar_t	  *ConvertStrTowStr(wchar_t *dest, size_t destBuffwCharsLen, const char *source, size_t sourceStrLenMax)
{
	const wchar_t *rc = '\0';

	if ((dest == '\0') || (source == '\0') || (destBuffwCharsLen < 1) || (sourceStrLenMax < 1))
		std::cout << "Error: ConvertStrTowStr() has invalid params" << std::endl;
	else
	{
		size_t   convertCnt = 0;
		size_t   sourceCnt = strnlen(source, sourceStrLenMax+1 ) + 1;	//if source is not terminated or is longer than sourceStrLenMax, then strnlen returns sourcewStrLenMax+1 so convertCnt != sourceCnt

		if ( (mbstowcs_s(&convertCnt, dest, destBuffwCharsLen, source, sourceStrLenMax) != 0 ) || (convertCnt != sourceCnt))
			std::cout << "Error: mbstowcs_s failed, " << std::endl;
		else
			rc = dest;
	}
	return rc;
}